ASHRAE

A Nagy energia előrejelző versenypályázat III


Mennyi energiát fogyaszt egy épület?

Bevezetés

Ebben a versenykiírásban a feladat olyan model fejlesztése, mely képes pontosan előrejelezni egy épület energia fogyasztását a követlező területeken: elektromosság, vízhűtés, gőz , illetve melegvíz. Az adatok több mint 1000 épület három évre visszamenő adatait tartalmazzák.

A kiírás célja, hogy a energiamegtakarítás jobb becslése révén motiválja a nagybefektetőket és pénzügyi intézményeket a területbe történő befektetésre, ezzel előrelendítve a hatékonyság kiépítését. Általánosságban az épületek energiahatékonyságának javítása illeszkedik napjaink egyik legmeghatározóbb agendájába, a klímaváltozás negatív következményei elleni küzdelemhez. Eszerint a fogyasztás visszafogása csökkentheti a környezeti terhelést, azon belül is különösképp az üvegházhatást okozó gázok kibocsátásának a visszafogását.

Az energiahatékonyság predikciójában rejlő legjelentősebb kihívás a kontrafaktuális állapot becslése, vagyis jelen esetben annak meghatározása, hogy mennyi energiát fogyasztott volna az adott épület abban az esetben, ha az energiafogyasztással kapcsolatos fejlesztések nem kerültek volna kivitelezésre. A gépi tanulás segítségével olyan model fejlesztése a cél, mely három évet felölelő energetikai mérési és időjárás adatok alapján képes pontos becslést adni az energiahatékonyság javulására vonatkozólag.

Jelen notebook jelentős mértékben támaszkodik az alábbi kernelekre:
https://www.kaggle.com/corochann/ashrae-training-lgbm-by-meter-type
https://www.kaggle.com/caesarlupum/ashrae-start-here-a-gentle-introduction
https://www.kaggle.com/c/ashrae-energy-prediction/notebooks

Fájlok:

train.csv

  • building_id - Idegen kulcs az épület metadata fájlhoz.

  • meter - A mérő azonosító kódja (0: elektromos áram (electricity), 1: hidegvíz (chilledwater), 2: gőz (steam), 3: melegvíz (hotwater). Nem minden épület rendelkezik minden mérő típussal.

  • timestamp - az időpont, amikor a mérés megvalósult

  • meter_reading - A célváltozó. Az energiafogyasztás kWh-ban kifejezve (vagy azzal ekvivalens). Valós adatok révén figyelembe kell venni a mérési hibát, mely a modellezési hibának az baseline szintjeként értelmezhető.

building_meta.csv

  • site_id - Helyszín, idegen kulcs az időjárás fájlhoz.

  • building_id - Idegen kulcs a training fájlhoz

  • primary_use - Az épület elsődleges tevékenységének kategóriája az EnergieStar ingatlan típus besorolása alapján

  • square_feet - Az épület bruttó területe

  • year_built - Az épület megynitásának időpontja

  • floor_count - Az épület emeleteinek a száma

weather_[train/test].csv

  • site_id - Idegen kulcs az időjárás fájlhoz.

  • air_temperature - Hőmérséklet Celsius fokban

  • cloud_coverage - Az arány, amilyen mértékben felhők borítják az eget

  • dew_temperature - Harmatpont Celsius fokban

  • precip_depth_1_hr - Csapadékmennyiség millimeterben

  • sea_level_pressure - Tengerszintre átszámított légnyomás millibárban

  • wind_direction - Szélirány iránytű szerinti fokban

  • wind_speed - Szélerősség m/s-ban

test.csv

  • row_id - Idegen kulcs az időjárás fájlhoz

  • building_id - Idegen kulcs a training fájlhoz

  • meter - A mérő azonosító kódja (0: elektromosság (electricity), 1: vízhűtés (chilledwater), 2: gőz (steam), 3: melegvíz (hotwater). Nem minden épület rendelkezik minden mérő típussal.

  • timestamp - az időpont, amikor a mérés megvalósult

sample_submission.csv

Az érvényes minta.

Csomagok importálása

In [1]:
# Csomagok importálása vizualizációhoz
import matplotlib.pyplot as plt
import seaborn as sns

# Csomagok importálása változók alakításához
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.preprocessing import LabelEncoder

# Csomagok importálása modellezéshez
from sklearn.metrics import mean_squared_error
import lightgbm as lgb
from sklearn.model_selection import KFold, StratifiedKFold
from tqdm import tqdm

# Általános csomagok importálása
import pandas as pd
import numpy as np
import os
from IPython.display import display_html
from datetime import datetime

Adatok betöltése

In [2]:
# Az elérési út megadása
root = 'C:\\Users\\zsolt\\Project_Github\\project_ASHRAE\\Data'
In [3]:
# Adatok betöltése
df_train = pd.read_csv(os.path.join(root, 'train.csv'))
df_test = pd.read_csv(os.path.join(root, 'test.csv'))
df_weather_train = pd.read_csv(os.path.join(root, 'weather_train.csv'))
df_weather_test = pd.read_csv(os.path.join(root, 'weather_test.csv'))
df_building = pd.read_csv(os.path.join(root, 'building_metadata.csv'))
In [4]:
# Gyors pillantás az adatokra
display(df_train.iloc[:10,:])
display(df_test.iloc[:10,:])
display(df_weather_train.iloc[:10,:])
display(df_weather_test.iloc[:10,:])
display(df_building.iloc[:10,:])
building_id meter timestamp meter_reading
0 0 0 2016-01-01 00:00:00 0.0
1 1 0 2016-01-01 00:00:00 0.0
2 2 0 2016-01-01 00:00:00 0.0
3 3 0 2016-01-01 00:00:00 0.0
4 4 0 2016-01-01 00:00:00 0.0
5 5 0 2016-01-01 00:00:00 0.0
6 6 0 2016-01-01 00:00:00 0.0
7 7 0 2016-01-01 00:00:00 0.0
8 8 0 2016-01-01 00:00:00 0.0
9 9 0 2016-01-01 00:00:00 0.0
row_id building_id meter timestamp
0 0 0 0 2017-01-01 00:00:00
1 1 1 0 2017-01-01 00:00:00
2 2 2 0 2017-01-01 00:00:00
3 3 3 0 2017-01-01 00:00:00
4 4 4 0 2017-01-01 00:00:00
5 5 5 0 2017-01-01 00:00:00
6 6 6 0 2017-01-01 00:00:00
7 7 7 0 2017-01-01 00:00:00
8 8 7 1 2017-01-01 00:00:00
9 9 8 0 2017-01-01 00:00:00
site_id timestamp air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 2016-01-01 00:00:00 25.0 6.0 20.0 NaN 1019.7 0.0 0.0
1 0 2016-01-01 01:00:00 24.4 NaN 21.1 -1.0 1020.2 70.0 1.5
2 0 2016-01-01 02:00:00 22.8 2.0 21.1 0.0 1020.2 0.0 0.0
3 0 2016-01-01 03:00:00 21.1 2.0 20.6 0.0 1020.1 0.0 0.0
4 0 2016-01-01 04:00:00 20.0 2.0 20.0 -1.0 1020.0 250.0 2.6
5 0 2016-01-01 05:00:00 19.4 NaN 19.4 0.0 NaN 0.0 0.0
6 0 2016-01-01 06:00:00 21.1 6.0 21.1 -1.0 1019.4 0.0 0.0
7 0 2016-01-01 07:00:00 21.1 NaN 21.1 0.0 1018.8 210.0 1.5
8 0 2016-01-01 08:00:00 20.6 NaN 20.0 0.0 1018.1 0.0 0.0
9 0 2016-01-01 09:00:00 21.1 NaN 20.6 0.0 1019.0 290.0 1.5
site_id timestamp air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 2017-01-01 00:00:00 17.8 4.0 11.7 NaN 1021.4 100.0 3.6
1 0 2017-01-01 01:00:00 17.8 2.0 12.8 0.0 1022.0 130.0 3.1
2 0 2017-01-01 02:00:00 16.1 0.0 12.8 0.0 1021.9 140.0 3.1
3 0 2017-01-01 03:00:00 17.2 0.0 13.3 0.0 1022.2 140.0 3.1
4 0 2017-01-01 04:00:00 16.7 2.0 13.3 0.0 1022.3 130.0 2.6
5 0 2017-01-01 05:00:00 15.6 2.0 12.8 0.0 1022.1 130.0 2.1
6 0 2017-01-01 06:00:00 15.0 0.0 12.8 0.0 1021.7 150.0 1.5
7 0 2017-01-01 07:00:00 15.0 2.0 13.3 0.0 1021.6 0.0 0.0
8 0 2017-01-01 08:00:00 13.3 0.0 12.2 0.0 1021.4 0.0 0.0
9 0 2017-01-01 09:00:00 12.2 4.0 11.7 0.0 1021.4 0.0 0.0
site_id building_id primary_use square_feet year_built floor_count
0 0 0 Education 7432 2008.0 NaN
1 0 1 Education 2720 2004.0 NaN
2 0 2 Education 5376 1991.0 NaN
3 0 3 Education 23685 2002.0 NaN
4 0 4 Education 116607 1975.0 NaN
5 0 5 Education 8000 2000.0 NaN
6 0 6 Lodging/residential 27926 1981.0 NaN
7 0 7 Education 121074 1989.0 NaN
8 0 8 Education 60809 2003.0 NaN
9 0 9 Office 27000 2010.0 NaN
In [5]:
# Alapinformációk lekérése az adatszetekről
df_train.info()
print('')
df_test.info()
print('')
df_weather_train.info()
print('')
df_weather_test.info()
print('')
df_building.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20216100 entries, 0 to 20216099
Data columns (total 4 columns):
building_id      int64
meter            int64
timestamp        object
meter_reading    float64
dtypes: float64(1), int64(2), object(1)
memory usage: 616.9+ MB

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 41697600 entries, 0 to 41697599
Data columns (total 4 columns):
row_id         int64
building_id    int64
meter          int64
timestamp      object
dtypes: int64(3), object(1)
memory usage: 1.2+ GB

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 139773 entries, 0 to 139772
Data columns (total 9 columns):
site_id               139773 non-null int64
timestamp             139773 non-null object
air_temperature       139718 non-null float64
cloud_coverage        70600 non-null float64
dew_temperature       139660 non-null float64
precip_depth_1_hr     89484 non-null float64
sea_level_pressure    129155 non-null float64
wind_direction        133505 non-null float64
wind_speed            139469 non-null float64
dtypes: float64(7), int64(1), object(1)
memory usage: 9.6+ MB

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 277243 entries, 0 to 277242
Data columns (total 9 columns):
site_id               277243 non-null int64
timestamp             277243 non-null object
air_temperature       277139 non-null float64
cloud_coverage        136795 non-null float64
dew_temperature       276916 non-null float64
precip_depth_1_hr     181655 non-null float64
sea_level_pressure    255978 non-null float64
wind_direction        264873 non-null float64
wind_speed            276783 non-null float64
dtypes: float64(7), int64(1), object(1)
memory usage: 19.0+ MB

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1449 entries, 0 to 1448
Data columns (total 6 columns):
site_id        1449 non-null int64
building_id    1449 non-null int64
primary_use    1449 non-null object
square_feet    1449 non-null int64
year_built     675 non-null float64
floor_count    355 non-null float64
dtypes: float64(2), int64(3), object(1)
memory usage: 68.0+ KB

Memóriahasználat csökkentése

A train és test adatszetek memóriahasználata jelentős, mely hatékonyan csökkenthető a változók konvertálásával

In [6]:
## Függvény a memóriahasználat csökkentéséhez. Forrás: https://www.kaggle.com/caesarlupum/ashrae-start-here-a-gentle-introduction
def reduce_mem_usage(df, verbose=True):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    start_mem = df.memory_usage().sum() / 1024**2    
    for col in df.columns:
        col_type = df[col].dtypes
        if col_type in numerics:
            c_min = df[col].min()
            c_max = df[col].max()
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)    
    end_mem = df.memory_usage().sum() / 1024**2
    if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
    return df
In [7]:
# A memóriahasználatot csökkentő függvény alkalmazása

df_train_red = reduce_mem_usage(df_train)
df_test_red = reduce_mem_usage(df_test)
df_weather_train_red = reduce_mem_usage(df_weather_train)
df_weather_test_red = reduce_mem_usage(df_weather_test)
df_building_red = reduce_mem_usage(df_building)
Mem. usage decreased to 289.19 Mb (53.1% reduction)
Mem. usage decreased to 596.49 Mb (53.1% reduction)
Mem. usage decreased to  3.07 Mb (68.1% reduction)
Mem. usage decreased to  6.08 Mb (68.1% reduction)
Mem. usage decreased to  0.03 Mb (60.3% reduction)
In [8]:
# Alapstatisztikák lekérése az adatok transzformáció előtti és utáni összehasonlításához:

# df_train
desc_dftrain = df_train.describe()
desc_dftrain_red = df_train_red.describe()

desc_dftrain_styler = desc_dftrain.style.set_table_attributes("style='display:inline'").set_caption('Eredeti df_train')
desc_dftrain_red_styler = desc_dftrain_red.style.set_table_attributes("style='display:inline'").set_caption('Transzformált df_train')

display_html(desc_dftrain_styler._repr_html_()+desc_dftrain_red_styler._repr_html_(), raw=True)

# df_test
desc_dftest = df_test.describe()
desc_dftest_red = df_test_red.describe()

desc_dftest_styler = desc_dftest.style.set_table_attributes("style='display:inline'").set_caption('Eredeti df_test')
desc_dftest_red_styler = desc_dftest_red.style.set_table_attributes("style='display:inline'").set_caption('Transzformált df_test')

display_html(desc_dftest_styler._repr_html_()+desc_dftest_red_styler._repr_html_(), raw=True)

# df_weather_train
desc_dfweather_train = df_weather_train.describe()
desc_dfweather_train_red = df_weather_train_red.describe()

desc_dfweather_train_styler = desc_dfweather_train.style.set_table_attributes("style='display:inline'").set_caption('Eredeti df_weather_train')
desc_dfweather_train_red_styler = desc_dfweather_train_red.style.set_table_attributes("style='display:inline'").set_caption('Transzformált df_weather_train')

display_html(desc_dfweather_train_styler._repr_html_()+desc_dfweather_train_red_styler._repr_html_(), raw=True)

# df_weather_test
desc_dfweather_test = df_weather_test.describe()
desc_dfweather_test_red = df_weather_test_red.describe()

desc_dfweather_test_styler = desc_dfweather_test.style.set_table_attributes("style='display:inline'").set_caption('Eredeti df_weather_test')
desc_dfweather_test_red_styler = desc_dfweather_test_red.style.set_table_attributes("style='display:inline'").set_caption('Transzformált df_weather_test')

display_html(desc_dfweather_test_styler._repr_html_()+desc_dfweather_test_red_styler._repr_html_(), raw=True)

# building
desc_dfbuilding = df_building.describe()
desc_dfbuilding_red = df_building_red.describe()

desc_dfbuilding_styler = desc_dfbuilding.style.set_table_attributes("style='display:inline'").set_caption('Eredeti df_building')
desc_dfbuilding_red_styler = desc_dfbuilding_red.style.set_table_attributes("style='display:inline'").set_caption('Transzformált df_building')

display_html(desc_dfbuilding_styler._repr_html_()+desc_dfbuilding_red_styler._repr_html_(), raw=True)
Eredeti df_train
building_id meter meter_reading
count 2.02161e+07 2.02161e+07 2.02161e+07
mean 799.278 0.662441 2117.12
std 426.913 0.930992 153236
min 0 0 0
25% 393 0 18.3
50% 895 0 78.775
75% 1179 1 267.984
max 1448 3 2.19047e+07
Transzformált df_train
building_id meter meter_reading
count 2.02161e+07 2.02161e+07 2.02161e+07
mean 799.278 0.662441 2117.12
std 426.913 0.930992 153236
min 0 0 0
25% 393 0 18.3
50% 895 0 78.775
75% 1179 1 267.984
max 1448 3 2.19047e+07
Eredeti df_test
row_id building_id meter
count 4.16976e+07 4.16976e+07 4.16976e+07
mean 2.08488e+07 807.582 0.664286
std 1.20371e+07 429.768 0.927807
min 0 0 0
25% 1.04244e+07 404.75 0
50% 2.08488e+07 900 0
75% 3.12732e+07 1194.25 1
max 4.16976e+07 1448 3
Transzformált df_test
row_id building_id meter
count 4.16976e+07 4.16976e+07 4.16976e+07
mean 2.08488e+07 807.582 0.664286
std 1.20371e+07 429.768 0.927807
min 0 0 0
25% 1.04244e+07 404.75 0
50% 2.08488e+07 900 0
75% 3.12732e+07 1194.25 1
max 4.16976e+07 1448 3
Eredeti df_weather_train
site_id air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
count 139773 139718 70600 139660 89484 129155 133505 139469
mean 7.47898 nan nan nan nan nan nan nan
std 4.60474 0 0 0 nan nan nan 0
min 0 -28.9062 0 -35 -1 968 0 0
25% 3 7.19922 0 0.600098 0 1012 80 2.09961
50% 7 15 2 8.29688 0 1016.5 190 3.09961
75% 11 22.2031 4 14.3984 0 1021 280 5
max 15 47.1875 9 26.0938 343 1046 360 19
Transzformált df_weather_train
site_id air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
count 139773 139718 70600 139660 89484 129155 133505 139469
mean 7.47898 nan nan nan nan nan nan nan
std 4.60474 0 0 0 nan nan nan 0
min 0 -28.9062 0 -35 -1 968 0 0
25% 3 7.19922 0 0.600098 0 1012 80 2.09961
50% 7 15 2 8.29688 0 1016.5 190 3.09961
75% 11 22.2031 4 14.3984 0 1021 280 5
max 15 47.1875 9 26.0938 343 1046 360 19
Eredeti df_weather_test
site_id air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
count 277243 277139 136795 276916 181655 255978 264873 276783
mean 7.48465 nan nan nan nan nan nan nan
std 4.61696 0 0 0 nan nan nan 0
min 0 -28.0938 0 -31.5938 -1 972 0 0
25% 3 7.19922 0 0.600098 0 1011.5 80 2.09961
50% 7 15 2 8.39844 0 1016.5 190 3.09961
75% 12 22.2031 4 15 0 1021 280 4.60156
max 15 48.3125 9 26.7031 597 1050 360 24.2031
Transzformált df_weather_test
site_id air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
count 277243 277139 136795 276916 181655 255978 264873 276783
mean 7.48465 nan nan nan nan nan nan nan
std 4.61696 0 0 0 nan nan nan 0
min 0 -28.0938 0 -31.5938 -1 972 0 0
25% 3 7.19922 0 0.600098 0 1011.5 80 2.09961
50% 7 15 2 8.39844 0 1016.5 190 3.09961
75% 12 22.2031 4 15 0 1021 280 4.60156
max 15 48.3125 9 26.7031 597 1050 360 24.2031
Eredeti df_building
site_id building_id square_feet year_built floor_count
count 1449 1449 1449 675 355
mean 6.95238 724 92111.8 inf 3.74023
std 5.00343 418.435 110770 31.0625 3.33398
min 0 0 283 1900 1
25% 3 362 23012 1949 1
50% 5 724 57673 1970 3
75% 13 1086 115676 1995 5
max 15 1448 875000 2017 26
Transzformált df_building
site_id building_id square_feet year_built floor_count
count 1449 1449 1449 675 355
mean 6.95238 724 92111.8 inf 3.74023
std 5.00343 418.435 110770 31.0625 3.33398
min 0 0 283 1900 1
25% 3 362 23012 1949 1
50% 5 724 57673 1970 3
75% 13 1086 115676 1995 5
max 15 1448 875000 2017 26

Adatszetek egyesítése

In [9]:
train = df_train_red.merge(df_building_red, on='building_id', how='left')
test = df_test_red.merge(df_building_red, on='building_id', how='left')

train = train.merge(df_weather_train_red, on=['site_id', 'timestamp'], how='left')
test = test.merge(df_weather_test_red, on=['site_id', 'timestamp'], how='left')

display(train.iloc[:10,:])
display(test.iloc[:10,:])
building_id meter timestamp meter_reading site_id primary_use square_feet year_built floor_count air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 0 2016-01-01 00:00:00 0.0 0 Education 7432 2008.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
1 1 0 2016-01-01 00:00:00 0.0 0 Education 2720 2004.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
2 2 0 2016-01-01 00:00:00 0.0 0 Education 5376 1991.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
3 3 0 2016-01-01 00:00:00 0.0 0 Education 23685 2002.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
4 4 0 2016-01-01 00:00:00 0.0 0 Education 116607 1975.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
5 5 0 2016-01-01 00:00:00 0.0 0 Education 8000 2000.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
6 6 0 2016-01-01 00:00:00 0.0 0 Lodging/residential 27926 1981.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
7 7 0 2016-01-01 00:00:00 0.0 0 Education 121074 1989.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
8 8 0 2016-01-01 00:00:00 0.0 0 Education 60809 2003.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
9 9 0 2016-01-01 00:00:00 0.0 0 Office 27000 2010.0 NaN 25.0 6.0 20.0 NaN 1019.5 0.0 0.0
row_id building_id meter timestamp site_id primary_use square_feet year_built floor_count air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 0 0 2017-01-01 00:00:00 0 Education 7432 2008.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
1 1 1 0 2017-01-01 00:00:00 0 Education 2720 2004.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
2 2 2 0 2017-01-01 00:00:00 0 Education 5376 1991.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
3 3 3 0 2017-01-01 00:00:00 0 Education 23685 2002.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
4 4 4 0 2017-01-01 00:00:00 0 Education 116607 1975.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
5 5 5 0 2017-01-01 00:00:00 0 Education 8000 2000.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
6 6 6 0 2017-01-01 00:00:00 0 Lodging/residential 27926 1981.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
7 7 7 0 2017-01-01 00:00:00 0 Education 121074 1989.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
8 8 7 1 2017-01-01 00:00:00 0 Education 121074 1989.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609
9 9 8 0 2017-01-01 00:00:00 0 Education 60809 2003.0 NaN 17.796875 4.0 11.703125 NaN 1021.5 100.0 3.599609

Adatok feltérképezése

Az adatok feltérképezése egy iteratív, végnélküli folyamat, mely során igyekszünk megérteni az adatban rejlő mintázatokat, összefüggéseket, trendeket, valamint anomáliákat alapvető statisztikai eljárások használata révén. A lentebbi elemzés betekintést nyújt a folyamatba, azonban korántsem tekinthető minden részletre kiterjedő vizsgálatnak, helyet hagyva az olvasó számára a további elemzésekre.

Hiányzó adatok vizsgálata

In [10]:
train_missing = train.drop('meter_reading', axis=1).count().divide(len(train)).round(4).sort_values()*100
test_missing = test.drop('row_id', axis=1).count().divide(len(test)).round(4).sort_values()*100


fig, ax = plt.subplots(figsize=(12, 8))
x = np.arange(len(train.drop('meter_reading', axis=1).columns))
bar_width = 0.4
b1 = ax.bar(x, train_missing, width=bar_width, color='indigo')
b2 = ax.bar(x + bar_width, test_missing, width=bar_width, color='orange')

# Fix the x-axes.
ax.set_xticks(x + bar_width / 2)
ax.set_xticklabels(train_missing.index, rotation=40)
ax.legend([b1, b2], ['Train', 'Test'])
ax.set_ylabel('%')
ax.set_title('A rendelkezésre álló adat százalékban kifejezve', fontsize=16);

A fenti ábra alapján látható, hogy három változó, a szintek száma, az építése éve, illetve a felhőtakaró nagysága esetében a rendelkezésre álló adatok mennyisége alacsony. Érdemes tehát utána nézni, hogy esetleg van-e bármilyen mintázat, ami az adathiányt magyarázhatná.

In [11]:
temp_df = train[train['floor_count'].isnull()]
print('Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik az épület szintje változó értéke:')
for columns in temp_df.columns:
    print('')
    print(str(columns) + str(':'))
    print(temp_df[columns].value_counts().head(5))
Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik az épület szintje változó értéke:

building_id:
1298    35136
1249    35136
1301    35128
1241    35116
1296    35115
Name: building_id, dtype: int64

meter:
0    9096083
1    3972549
2    2604116
3    1036419
Name: meter, dtype: int64

timestamp:
2016-12-27 20:00:00    1960
2016-12-27 22:00:00    1960
2016-12-27 17:00:00    1960
2016-12-27 19:00:00    1960
2016-12-28 00:00:00    1959
Name: timestamp, dtype: int64

meter_reading:
0.000000     1620375
2.930700       23181
8.792100       21787
36.599998      21447
5.861400       21309
Name: meter_reading, dtype: int64

site_id:
13    2711763
9     2679323
2     2530312
14    2501506
3     2370097
Name: site_id, dtype: int64

primary_use:
Education                        6254313
Office                           4038322
Lodging/residential              1993811
Entertainment/public assembly    1796262
Public services                  1305198
Name: primary_use, dtype: int64

square_feet:
387638    61488
200933    45808
42755     41156
53130     35136
24456     35136
Name: square_feet, dtype: int64

year_built:
1964.0    181767
2006.0    177255
1975.0    173100
1968.0    170299
2007.0    150289
Name: year_built, dtype: int64

floor_count:
Series([], Name: floor_count, dtype: int64)

air_temperature:
24.406250    361794
25.000000    348636
25.593750    348507
23.296875    347393
23.906250    343745
Name: air_temperature, dtype: int64

cloud_coverage:
0.0    4989163
2.0    2094676
4.0    1649861
8.0     519745
6.0     505327
Name: cloud_coverage, dtype: int64

dew_temperature:
22.203125    334453
22.796875    325284
13.898438    321502
18.906250    315154
15.601562    312399
Name: dew_temperature, dtype: int64

precip_depth_1_hr:
 0.0    13049227
-1.0     1009207
 3.0      236306
 5.0      108254
 8.0       65712
Name: precip_depth_1_hr, dtype: int64

sea_level_pressure:
1016.0    521813
1015.5    489760
1016.5    484702
1015.0    482804
1014.5    479575
Name: sea_level_pressure, dtype: int64

wind_direction:
0.0      2169811
170.0     526021
180.0     485891
300.0     473046
190.0     471364
Name: wind_direction, dtype: int64

wind_speed:
0.000000    2172986
2.099609    2007007
2.599609    1864044
1.500000    1805991
3.099609    1656000
Name: wind_speed, dtype: int64
In [12]:
temp_df = train[train['year_built'].isnull()]
print('Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik az épület építésének éve változó értéke:')
for columns in temp_df.columns:
    print('')
    print(str(columns) + str(':'))
    print(temp_df[columns].value_counts().head(5))
Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik az épület építésének éve változó értéke:

building_id:
1249    35136
1298    35136
1301    35128
1241    35116
1297    35115
Name: building_id, dtype: int64

meter:
0    6470035
1    2819559
2    2141016
3     697035
Name: meter, dtype: int64

timestamp:
2016-11-04 07:00:00    1400
2016-11-08 20:00:00    1400
2016-11-04 14:00:00    1399
2016-10-06 14:00:00    1399
2016-09-30 15:00:00    1399
Name: timestamp, dtype: int64

meter_reading:
0.000000     1093734
36.599998      21282
24.400000      20438
61.000000      19960
20.000000      19878
Name: meter_reading, dtype: int64

site_id:
13    2711763
9     2679323
14    2501506
3     1236835
6      668133
Name: site_id, dtype: int64

primary_use:
Education                        4114086
Office                           3318685
Entertainment/public assembly    1423388
Public services                  1161377
Lodging/residential              1078064
Name: primary_use, dtype: int64

square_feet:
42755     41156
159384    35136
171084    35136
72958     35135
65000     35134
Name: square_feet, dtype: int64

year_built:
Series([], Name: year_built, dtype: int64)

floor_count:
1.0    685256
2.0    234406
4.0    111445
5.0     78648
6.0     64155
Name: floor_count, dtype: int64

air_temperature:
23.296875    255409
24.406250    254531
25.000000    248463
23.906250    246831
25.593750    243513
Name: air_temperature, dtype: int64

cloud_coverage:
0.0    4060677
2.0    1238761
4.0     957189
8.0     382637
6.0     298279
Name: cloud_coverage, dtype: int64

dew_temperature:
22.203125    272918
22.796875    262123
21.703125    247595
23.296875    235624
18.906250    232058
Name: dew_temperature, dtype: int64

precip_depth_1_hr:
 0.0    10094933
-1.0      693063
 3.0      175506
 5.0       83411
 8.0       52077
Name: precip_depth_1_hr, dtype: int64

sea_level_pressure:
1016.0    385133
1014.5    365358
1015.0    365346
1015.5    362091
1016.5    359255
Name: sea_level_pressure, dtype: int64

wind_direction:
0.0      1629834
170.0     422638
180.0     373749
160.0     349287
190.0     345307
Name: wind_direction, dtype: int64

wind_speed:
0.000000    1633359
2.099609    1439087
1.500000    1331974
2.599609    1318578
3.099609    1174236
Name: wind_speed, dtype: int64
In [13]:
temp_df = train[train['cloud_coverage'].isnull()]
print('Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik a felhőtakaró mértéke változó értéke:')
for columns in temp_df.columns:
    print('')
    print(str(columns) + str(':'))
    print(temp_df[columns].value_counts().head(5))
Leggyakoribb kategóriák/értékek változónként azon adatpontok esetében, ahol hiányzik a felhőtakaró mértéke változó értéke:

building_id:
1032    26346
802     26343
801     26343
800     26343
793     26343
Name: building_id, dtype: int64

meter:
0    5329652
1    1742296
2    1247671
3     505746
Name: meter, dtype: int64

timestamp:
2016-11-23 11:00:00    2363
2016-11-23 00:00:00    2359
2016-09-29 08:00:00    2359
2016-11-23 01:00:00    2358
2016-04-07 15:00:00    2343
Name: timestamp, dtype: int64

meter_reading:
0.0      849623
100.0     11682
10.0      11617
20.0      10829
30.0       9576
Name: meter_reading, dtype: int64

site_id:
13    1327342
9     1055963
3      983668
14     944603
15     940006
Name: site_id, dtype: int64

primary_use:
Education                        3682525
Office                           1905997
Entertainment/public assembly     949881
Lodging/residential               910389
Public services                   715976
Name: primary_use, dtype: int64

square_feet:
150695    52317
64583     43523
387638    26810
127632    26346
731945    26343
Name: square_feet, dtype: int64

year_built:
1976.0    355734
1966.0    132851
1960.0    106672
1964.0    105446
1919.0    104027
Name: year_built, dtype: int64

floor_count:
1.0    449503
2.0    381871
6.0    211088
3.0    195967
4.0    187069
Name: floor_count, dtype: int64

air_temperature:
15.000000    178650
24.406250    167141
23.906250    159986
23.296875    159649
20.000000    158088
Name: air_temperature, dtype: int64

cloud_coverage:
Series([], Name: cloud_coverage, dtype: int64)

dew_temperature:
10.000000    175962
15.000000    165488
23.296875    165010
19.406250    162216
20.000000    160812
Name: dew_temperature, dtype: int64

precip_depth_1_hr:
 0.0    5019736
-1.0     924313
 3.0     218676
 5.0     101109
 8.0      61113
Name: precip_depth_1_hr, dtype: int64

sea_level_pressure:
1015.0    250940
1014.0    250020
1016.0    248823
1015.5    247300
1014.5    238693
Name: sea_level_pressure, dtype: int64

wind_direction:
0.0      726454
170.0    302754
180.0    291103
270.0    266000
160.0    265227
Name: wind_direction, dtype: int64

wind_speed:
2.099609    911204
2.599609    894433
3.099609    847853
3.599609    814861
1.500000    765024
Name: wind_speed, dtype: int64

A kapott eredmények alapján érdemes lesz külön figyelmet fordítani a 13-as telekre, valamint az 1249-es és 1298-as azaonosítóval rendelkező épületekre, mivel esetükben adathiány több változó esetében is fennáll.

Egyváltozós elemzés

A célváltozó eloszlása erősen ferde a kiugró értékek miatt.

In [14]:
ax = sns.boxplot(y='meter_reading',
                 data=train, 
                 width=0.5,
                 palette="BuPu")
ax.set(ylabel = 'Célváltozó eloszlása')
ax.set_title('Kiugró értékek a célváltozó (Energiafogyasztás KWh-ban) esetében', fontsize=12);
In [15]:
# Kiugró értékek vizsgálata az Interkvartilis terjedelem (IQR) érték alapján

def outliers_iqr(x, mplyr):
    quartile_1, quartile_3 = np.percentile(x, [25, 75])
    iqr = quartile_3 - quartile_1
    lower_bound = quartile_1 - (iqr * mplyr)
    upper_bound = quartile_3 + (iqr * mplyr)
    return list(np.where((x > upper_bound) | (x < lower_bound)))

indexes_outlier = outliers_iqr(train['meter_reading'], 1.5)[0]
train_outliers = train[train.index.isin(indexes_outlier)]
train_no_outliers = train.drop(train.index[indexes_outlier])
print('Kiugró értékek száma a célváltozóban: ' + str(len(train_outliers)))
Kiugró értékek száma a célváltozóban: 2473655
In [16]:
bins1 = np.linspace(min(train_no_outliers['meter_reading']), max(train_no_outliers['meter_reading']), 25)
sns.distplot(train_no_outliers['meter_reading'], bins=bins1)
plt.figsize = [6,6]

plt.title('A célváltozó (Energiafogyasztás KWh-ban) eloszlása a kiugró értékek eltávolítása után')
plt.ylabel('Sűrűség')
plt.xlabel('Energiafogyasztás KWh-ban')
plt.show()

bins2 = np.linspace(min(train_outliers['meter_reading']), max(train_outliers['meter_reading']), 25)
sns.distplot(train_outliers['meter_reading'], bins=bins2)
plt.figsize = [6,6]

plt.title('A célváltozó (Energiafogyasztás KWh-ban) eloszlása a kiugró értékek esetében')
plt.ylabel('Sűrűség')
plt.xlabel('Energiafogyasztás KWh-ban')
plt.show()
In [17]:
# A célváltozó eloszlásának lekérése logaritmikus transzformációt követően
bins = np.linspace(min(np.log1p(train["meter_reading"].values)), max(np.log1p(train["meter_reading"].values)), 25)
sns.distplot(np.log1p(train['meter_reading']), bins=bins)
plt.figsize = [6,6]
plt.title('A célváltozó (Energiafogyasztás KWh-ban) eloszlása logtranszformáció után kiugró értékekkel')
plt.ylabel('Sűrűség')
plt.xlabel('Energiafogyasztás KWh-ban')
plt.show()
In [18]:
# A célváltozó eloszlásának lekérése logaritmikus transzformációt követően
bins = np.linspace(min(np.log1p(train_no_outliers["meter_reading"].values)), max(np.log1p(train_no_outliers["meter_reading"].values)), 25)
sns.distplot(np.log1p(train_no_outliers['meter_reading']), bins=bins)
plt.figsize = [6,6]
plt.title('A célváltozó (Energiafogyasztás KWh-ban) eloszlása logtranszformáció után')
plt.ylabel('Sűrűség')
plt.xlabel('Energiafogyasztás KWh-ban')
plt.show()

A változó eloszlása a logtranszformáció és a kiugró értékek kizárása után is erősen asszimmetrikus

A független változók elemzése

In [19]:
cat_features_df = train[['meter', 'site_id', 'primary_use', 'floor_count']]
num_features_df = train[['square_feet', 'year_built', 'air_temperature', 'cloud_coverage', 'dew_temperature',
                                    'precip_depth_1_hr', 'sea_level_pressure', 'wind_direction', 'wind_speed']]
In [20]:
for features in cat_features_df:
    ax = sns.countplot(x=features, data=cat_features_df, palette=("PRGn"))
    ax.set_title(str(str('Egyes kategóriákhoz tartozó elemek száma') + str(': ') + str(features)), fontsize=14)
    ax.set_ylabel('Elemek száma')
    ax.set_xlabel('Kategóriák')
    if features == 'primary_use':
        ax.tick_params(axis='x', rotation=60)
    plt.show()

Főbb megállapítások:

  • Mérő típus - Legtöbb adat elekromos áram fogyasztással kapcsolatosan, míg legkevesebb a melegvíz mérőhöz áll rendelkezésre

  • Helyszín - Legtöbb adat a 13-as, a 9-es és a 2-es telkeken lévő épületekkel kapcsolatosan található az adatszetben, míg a legkevesebb a 11 és 12-es telekhez tartozó épületekről áll rendelkezésre

  • Épület típus - A legtöbb esetben az épület besorolása oktatási kategóriába esett, míg a második leggyakoribb az iroda besorolosú volt

  • Szintek száma - Az épületek többsége egy vagy kétszintes volt

In [21]:
for features in num_features_df:
    num_features_df.hist(column=features, grid=False, figsize=(8,10), layout=(3,1), sharex=True, color='#411B46', rwidth=0.8)
    

Főbb megállapítások:

  • Alapterület - Számos épület alapterülete meghaladja 200000 négyzetlábat

  • Építési idő - Az épületek jelentős része az 1960-80-as évek közötti időszakban épült, valószínűsíthatően alacsonyabb energetikai hatékonyság mellett

  • léghőmérséklet - A levegőhőmérséklet eloszlása közel normális, ami egyrészt köszönhető az egy évet felölelő mérési időszaknak, illetve a helyszínek geográfiai szétszórtságának

A változók többváltozós elemzése

Adott mérőtípushoz tartozó mérések száma

In [22]:
target_count = train['meter'].value_counts()
target_count.index = ['Elektromos áram', 'Hidegvíz', 'Gőz', 'Melegvíz']
target_count = target_count.sort_values().to_frame().reset_index()

ax = sns.barplot(y= "meter", x = "index", data = target_count, palette=("BuPu"))
sns.set(rc={'figure.figsize':(10,6)})
ax.set(xlabel='Mérőtípus', ylabel='Mérések száma (egység = 10 millió)')
ax.set_title('Adott mérőtípushoz tartozó mérések száma', fontsize=14)
Out[22]:
Text(0.5, 1.0, 'Adott mérőtípushoz tartozó mérések száma')

Összes energiafogyasztás mérőtípusonként

In [23]:
target_sum = train.groupby(['meter'])['meter_reading'].sum()
target_sum.index = ['Elektromos áram', 'Hidegvíz', 'Gőz', 'Melegvíz']
target_sum = target_sum.sort_values().to_frame().reset_index()

ax = sns.barplot(x="index", y = "meter_reading", data = target_sum, palette=("BuPu"))
sns.set(rc={'figure.figsize':(10,6)})
ax.set(xlabel='Mérőtípus', ylabel='Energiafogyasztás')
ax.set_title('Adott mérőtípushoz tartozó összfogyasztás', fontsize=14)
Out[23]:
Text(0.5, 1.0, 'Adott mérőtípushoz tartozó összfogyasztás')

Míg a gőz esetében a mérések száma nagyjából hatoda a elektromos áram fogyasztással kapcsolatban rendelkezésre álló mérések számának, az összfogyasztás tekintetében a gőz mérőtípushoz kiugróan magas fogyasztás társul.

In [24]:
for meter_type in sorted(train['meter'].unique().tolist()):
    meter_reading = np.log1p(train[train.meter == meter_type]['meter_reading'])
    sns.kdeplot(meter_reading, shade=True)

plt.legend(['Elektromos áram', 'Hidegvíz', 'Gőz', 'Melegvíz'], prop={'size': 12})
plt.title('Célváltozó (Energiafogyasztás KWh-ban) eloszlása mérőtípus alapján',  fontsize=14)
plt.ylabel('Sűrűség')  
Out[24]:
Text(0, 0.5, 'Sűrűség')

Az energiafogasztás (logaritmusának) eloszlása a különböző mérőtípusok szerinti bontásban rámutat arra, hogy nagy számban találhatunk zéró értéket az adatszettben, mely az elektromos áram esetében a legkevesebb, míg a melegvíz esetében a legtöbb. Az eloszlás némi eltérést mutat az egyes mérőtípusok esetében, főként az elektromos áram tekintetében, míg a hidegvíz és a gőz mőrő szerinti fogyasztás eloszlása meglehetősen hasonló. Összefoglalva, a mérőtípus változójának bevonása az elemzésbe indokolt az eltérő eloszlások láttán.

Az idő aspektus vizsgálata a célváltozó függvényében

In [25]:
train["timestamp"] = pd.to_datetime(train["timestamp"])
test["timestamp"] = pd.to_datetime(test["timestamp"])
train["hour"] = train["timestamp"].dt.hour
train["day"] = train["timestamp"].dt.day
train["weekend"] = train["timestamp"].dt.weekday
train["month"] = train["timestamp"].dt.month
In [26]:
train['log_meter_reading'] = np.log1p(train['meter_reading'])
fig, ax = plt.subplots(figsize=(10,5))
train.groupby('hour')['meter_reading'].median().plot(kind='line', color="darkblue", figsize=(10,6), label=False)
ax.set_ylabel('Energiafogyasztás középértéke')
ax.set_xlabel('Óra')
plt.title("Az Energiafogyasztás középértéke óránként",  fontsize=14)
plt.show()
In [27]:
fig, axes = plt.subplots(1, 1, figsize=(14, 6), dpi=100)
train[['timestamp', 'meter_reading']].set_index('timestamp').resample('H').mean()['meter_reading'].plot(ax=axes, label='Óránként', alpha=0.8).set_ylabel('Energiafogyasztás', fontsize=14);
train[['timestamp', 'meter_reading']].set_index('timestamp').resample('D').mean()['meter_reading'].plot(ax=axes, label='Naponként', alpha=1).set_ylabel('Energiafogyasztás', fontsize=14);
axes.set_title('Átlagos energiafogyasztás óránként és naponként', fontsize=16);
axes.legend()
Out[27]:
<matplotlib.legend.Legend at 0x22e9a0a1c48>

Az átlagos energiafogyasztás ábrája meglehetősen furán fest, március után jelentős ugrás látható, míg június közepétől szinte 0 közelébe esik. Érdemes tehát egy közelebbi pillantást vetni rá.

In [28]:
title_list = list()
for sites in range(train['site_id'].nunique()):
    title_list.append(str('Helyszín ' + str(sites) + str(' energiafogyasztásának változása az idő függvényében')))
    
train.groupby(['timestamp', 'site_id'])['meter_reading'].mean().unstack().plot(subplots=True, layout=(8,2), figsize=(20,35), title=title_list)
Out[28]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E96326B88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E9616C6C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8AC16A48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A459388>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A547E88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8AAF3448>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A52FE08>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E902D8E08>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A4DBAC8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A409248>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A053788>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8D9CC8C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A360C48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A49D908>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A3DEC48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022E8A355948>]],
      dtype=object)

Egyrészről, az év eleji alacsony átlagos energiafogyasztásért részben az első helyszín (Site 0) április előtti adatainak hiánya, részben pedig a 6-os helyszín valószínűsíthető adathinya okolható. Másrészről, a 13-as helyszín ábrája nagyrészt hasonlít a teljes minta ábrájához. Amennyiben visszaemlékszünk arra, hogy a legtöbb mérési adat ezen a helyszínen lévő épületekről áll rendelkezésre, illetve az átlagos energiafogyasztás jelentősen nagyobb ezen a helyszínen, akkor könnyen belátható jelentős hatása a teljes mintára. Érdemes tehát külön megvizsgálni a 6-os helyszínt, illetve a 13-as helyszínt arra vonatkozólag, hogy az egyes épülettípusoknak miképpen alakul ezen a területeken az energiafogyasztása.

In [29]:
site_13_df = train[train.site_id == 13]
    
site_13_df.groupby(['timestamp', 'primary_use'])['meter_reading'].mean().unstack().plot(subplots=True, layout=(8,2), figsize=(20,35), title='A 13-as helyszín energiafogyasztásának változása elsődleges használat szerint:')
Out[29]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA2088E88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA20DA0C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA20E8248>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA1E8EA88>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA1EC7348>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA1EFE188>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA1F38108>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA1F6F088>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA2236B08>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA226FB88>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA22CB048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA22FFF48>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA2337F08>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA236FF08>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA23AD048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA23E5208>]],
      dtype=object)

A fentiak alapján az oktatási intézmények között kell tovább kutatodni. Lássuk az ehhez a kategóriához tartozó épületeket.

In [30]:
site_13_df = train[(train.site_id == 13) & (train.primary_use == 'Education')]
    
site_13_df.groupby(['timestamp', 'building_id'])['meter_reading'].mean().unstack().plot(subplots=True, layout=(20,2), figsize=(20,35), title='A 13-as helyszínhez tartozó oktatási épületek energiafogyasztásának változása:')
Out[30]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EB543D488>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EB54EE688>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EB5501788>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA768A048>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA76C28C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA76FA848>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7732808>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA776A788>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7785248>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA77BE2C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7828E88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA784B688>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7883648>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA78BB688>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA78F4608>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA792E5C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7965608>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA799F508>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA79D94C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7A104C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7A494C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7A80488>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7AB93C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7AF1408>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7B29388>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7B62348>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7B9B4C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7BD25C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7C0B788>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7C44988>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7C7CB48>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7CB5D08>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7CEEEC8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7D290C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7D61288>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7D9D488>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7DD4648>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7E0C7C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7E45988>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EA7E7CB88>]],
      dtype=object)

Az 1099-es számú épület egyfajta "általános átlagként" funkcionál, nélküle az adatok időbeni eloszlása jelentősen változik:

In [31]:
site_6_df = train[train.site_id == 6]
    
site_6_df.groupby(['timestamp', 'primary_use'])['meter_reading'].mean().unstack().plot(subplots=True, layout=(8,2), figsize=(20,35), title='A 6-os helyszín energiafogyasztásának változása elsődleges használat szerint:')
Out[31]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBEB006C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBF1F21C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBF1FE048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCADB8C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCB13188>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCB48A08>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCB99F88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCBB97C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCBD6308>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCC0C588>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCC64D88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCC9DEC8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCCD80C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCD132C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCD4A488>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBCD82648>]],
      dtype=object)

A 6-os helyszín esetében a szórakoztatás kategóriát érdemes górcső alá venni:

In [32]:
site_6_df = train[(train.site_id == 6) & (train.primary_use == 'Entertainment/public assembly')]
    
site_6_df.groupby(['timestamp', 'building_id'])['meter_reading'].mean().unstack().plot(subplots=True, layout=(2,2), figsize=(20,10), title='A 13-as helyszínhez tartozó oktatási épületek energiafogyasztásának változása:')
Out[32]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD80E988>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD85F308>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD878FC8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD003388>]],
      dtype=object)

A fentebbi ábra alapjá a 778-as és a 783-as épületek esetében nincs mérési adatunk az év nagyrészéről.

In [33]:
train_whtout_fliers =  train[(train.building_id != 1099) & (train.building_id != 778) & (train.building_id != 783)]

fig, axes = plt.subplots(1, 1, figsize=(14, 6), dpi=100)
train_whtout_fliers[['timestamp', 'meter_reading']].set_index('timestamp').resample('H').mean()['meter_reading'].plot(ax=axes, label='Óránként', alpha=0.8).set_ylabel('Energiafogyasztás', fontsize=14);
train_whtout_fliers[['timestamp', 'meter_reading']].set_index('timestamp').resample('D').mean()['meter_reading'].plot(ax=axes, label='Naponként', alpha=1).set_ylabel('Energiafogyasztás', fontsize=14);
axes.set_title('Átlagos energiafogyasztás óránként és naponként a 778-as, a 783-as és az 1099-es épület nélkül', fontsize=16);
axes.legend()
Out[33]:
<matplotlib.legend.Legend at 0x22ebd645108>

A jelentős adathiánnyal rendelkező épületek kizárása után az energiafogyasztás éves alakulása már természetesebb képet mutat, magasabb átlagos értékekkel a hideg téli és forró nyári hónapokban. A továbbiakban folytassuk az elemzést ezen épületek nélkül.

In [34]:
train = train_whtout_fliers
In [35]:
fig, ax = plt.subplots(figsize=(10,5))
ax = sns.boxplot(y="weekend", x="log_meter_reading", data=train, orient="h", palette="PuBu", showfliers=False)
ax.set_ylabel('Hét napjai')
ax.set_xlabel('Energiafogyasztás logaritmusának eloszlása')
ax.set_yticklabels(['Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat', 'Vasárnap'])
plt.title('Az Energiafogyasztás logaritmusának eloszlása a hét napjai szerinti bontásban kiugró értékek nélkül',  fontsize=14)
plt.show()
In [36]:
meter_types = ['Elektromos áram', 'Hidegvíz', 'Gőz', 'Melegvíz']
for meter_type in sorted(train['meter'].unique().tolist()):
    filtered_train = train[train.meter == meter_type]
    ax = filtered_train[['timestamp','log_meter_reading']].set_index('timestamp').resample("D")['log_meter_reading'].median().plot(kind='line', color="darkblue", figsize=(10,6), alpha=0.8, label=False)
    filtered_train[['timestamp','log_meter_reading']].set_index('timestamp').resample("H")['log_meter_reading'].median().plot(kind='line', color="darkblue", figsize=(10,6), alpha=0.3, label=False)
    ax2 = ax.twinx()
    filtered_train[['timestamp','air_temperature']].set_index('timestamp').resample("D")['air_temperature'].median().plot(ax=ax2, kind='line', color="darkred", figsize=(10,6), alpha=0.8, label=False)
    filtered_train[['timestamp','air_temperature']].set_index('timestamp').resample("H")['air_temperature'].median().plot(ax=ax2, kind='line', color="darkred", figsize=(10,6), alpha=0.3, label=False)
    ax.figure.legend(['Energiafogyasztás középértéke (nap és óra)', 'Levegőhőmérséklet középértéke (nap és óra)'], loc='lower right')
    ax.set_ylabel('Energiafogyasztás logaritmusának középértéke'); ax2.set_ylabel('Levegőhőmérséklet középértéke')
    ax.set_xlabel("Mérés ideje")
    plt.title("Az Energiafogyasztás és levegőhőmérséklet változása az idő függvényében, " + str('Mérőtípus: ') + str(meter_types[meter_type]),  fontsize=14)
    plt.show()
In [37]:
ax = train[['timestamp','log_meter_reading']].set_index('timestamp').resample("H")['log_meter_reading'].mean().plot(kind='line',figsize=(10,6), alpha=0.7, label='Összes energiafogyasztás óránként')
ax2 = ax.twinx()
train[['timestamp','air_temperature']].set_index('timestamp').resample("H")['air_temperature'].median().plot(ax=ax2, kind='line', color="orange", figsize=(10,6), alpha=0.7, label='Levegőhőmérséklet középértéke óránként')
plt.legend()
plt.xlabel("Mérés ideje")
plt.ylabel("Átlagos energiafogyasztás KWh-ban")
plt.title("Az Energiafogyasztás és levegőhőmérséklet változása az idő függvényében")
Out[37]:
Text(0.5, 1.0, 'Az Energiafogyasztás és levegőhőmérséklet változása az idő függvényében')

A használati típus szerinti energiafogyasztás vizsgálata

In [38]:
sorted(train['primary_use'].unique().tolist())
fig, ax = plt.subplots(figsize=(10,12))
ax = sns.boxplot(y="primary_use", x="log_meter_reading", data=train, orient="h", palette="PuBu", showfliers=False)
ax.set_ylabel('Elsődleges használati típus')
ax.set_xlabel('Energiafogyasztás logaritmusának eloszlása')
plt.title('Az Energiafogyasztás logaritmusának eloszlása elsődleges használati típus szerint',  fontsize=14)
plt.show()
In [39]:
train.groupby(['hour', 'primary_use'])['meter_reading'].median().unstack().plot(subplots=True, layout=(4,4))
Out[39]:
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD355988>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD38E048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD3A90C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD3DE948>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD658208>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD68DA88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD6C7508>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD700508>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD716F88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD757048>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBD8994C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBF2313C8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000022EBF269388>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EC07E23C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EC081B348>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x0000022EC0853308>]],
      dtype=object)

Energiafogyasztás vizsgálata épületenként

In [40]:
train.groupby(['building_id'])['meter_reading'].sum().plot()
Out[40]:
<matplotlib.axes._subplots.AxesSubplot at 0x22ec23327c8>

Változók alakítása

A modellezés előtt három lényeges teendőnk van az adatok alakítása kapcsán:

  • Anomáliák eltávolítása

  • Hiányzó időjárási adatok pótlása

  • Időzóna korrigálása

Anomáliák kezelése

Ahogy korábban láthattuk, az első helyszín (Site 0) április előtti mérési adatai nem megfelelőek. Ezen adatok más mértékegységben kerültek megadásra, ahogy ez ezen a fórumon kifejtésre került: https://www.kaggle.com/c/ashrae-energy-prediction/discussion/119261. Javítsuk őket!

In [41]:
train[(train.building_id <= 104) & (train.meter == 0) & (train.timestamp <= "2016-05-20")].plot(x='timestamp', y='meter_reading')
Out[41]:
<matplotlib.axes._subplots.AxesSubplot at 0x22ec23754c8>
In [42]:
train.loc[((train.building_id <= 104) & (train.meter == 0) & (train.timestamp <= "2016-05-20")),'meter_reading'] = train.loc[((train.building_id <= 104) & (train.meter == 0) & (train.timestamp <= "2016-05-20")),'meter_reading']* 0.2931

A változás a mértékegységben látható:

In [43]:
train[(train.building_id <= 104) & (train.meter == 0) & (train.timestamp <= "2016-05-20")].plot(x='timestamp', y='meter_reading')
Out[43]:
<matplotlib.axes._subplots.AxesSubplot at 0x22ece135508>

A modellezés szempontjából további problémát jelentenek a kiugró értékek, melyek a model tanítása során félrevezetők lehetnek, rontva a model általánosíthatóságát (csökkentve a predikció pontosságát űj adatokon). A kiugró értékeket helyszínenként külön-külön vizsgálhatjuk. Erre ezen bevezető alkalmával nem kerül sor.

Hiányzó időjárási adatok pótlása

In [44]:
print(df_weather_train_red.isna().sum())
print(df_weather_test_red.isna().sum())
site_id                   0
timestamp                 0
air_temperature          55
cloud_coverage        69173
dew_temperature         113
precip_depth_1_hr     50289
sea_level_pressure    10618
wind_direction         6268
wind_speed              304
dtype: int64
site_id                    0
timestamp                  0
air_temperature          104
cloud_coverage        140448
dew_temperature          327
precip_depth_1_hr      95588
sea_level_pressure     21265
wind_direction         12370
wind_speed               460
dtype: int64
In [45]:
# Adatok pótlása interpoláció segítségével
df_weather_train_red = df_weather_train_red.groupby('site_id').apply(lambda group: group.interpolate(limit_direction='both'))
df_weather_test_red = df_weather_test_red.groupby('site_id').apply(lambda group: group.interpolate(limit_direction='both'))
In [46]:
print(df_weather_train_red.isna().sum())
print(df_weather_test_red.isna().sum())
site_id                   0
timestamp                 0
air_temperature           0
cloud_coverage        17228
dew_temperature           0
precip_depth_1_hr     26273
sea_level_pressure     8755
wind_direction            0
wind_speed                0
dtype: int64
site_id                   0
timestamp                 0
air_temperature           0
cloud_coverage        33146
dew_temperature           0
precip_depth_1_hr     51807
sea_level_pressure    17241
wind_direction            0
wind_speed                0
dtype: int64

Az interpoláció sikeresnek mutatkozott az adathiány csökkentésében, de továbbra is számos változó esetében jelentős maradt a hiányzó adatok mennyisége. A további adatpótlást MICE módszerrel végezzük.

In [47]:
df_weather_train_red["timestamp"] = pd.to_datetime(df_weather_train_red["timestamp"])
df_weather_test_red["timestamp"] = pd.to_datetime(df_weather_test_red["timestamp"])

imp = IterativeImputer(random_state=0)
imp.fit(df_weather_train_red.loc[:, ~df_weather_train.columns.isin(['site_id', 'timestamp'])])
imp.fit(df_weather_test_red.loc[:, ~df_weather_train.columns.isin(['site_id', 'timestamp'])])
df_weather_train_imp = imp.transform(df_weather_train_red.loc[:, ~df_weather_train_red.columns.isin(['site_id', 'timestamp'])])
df_weather_test_imp = imp.transform(df_weather_test_red.loc[:, ~df_weather_test_red.columns.isin(['site_id', 'timestamp'])])

columns = ['air_temperature', 'cloud_coverage', 'dew_temperature', 'precip_depth_1_hr',  'sea_level_pressure',
'wind_direction',  'wind_speed']
df_weather_train_imp = pd.DataFrame(df_weather_train_imp)
df_weather_train_imp.columns = columns
df_weather_train_imp = df_weather_train_imp.assign(site_id = df_weather_train_red["site_id"],  timestamp=df_weather_train_red["timestamp"])

df_weather_test_imp = pd.DataFrame(df_weather_test_imp)
df_weather_test_imp.columns = columns
df_weather_test_imp = df_weather_test_imp.assign(site_id = df_weather_test_red["site_id"],  timestamp=df_weather_test_red["timestamp"])
In [48]:
print(df_weather_train_imp.isna().sum())
print(df_weather_test_imp.isna().sum())
air_temperature       0
cloud_coverage        0
dew_temperature       0
precip_depth_1_hr     0
sea_level_pressure    0
wind_direction        0
wind_speed            0
site_id               0
timestamp             0
dtype: int64
air_temperature       0
cloud_coverage        0
dew_temperature       0
precip_depth_1_hr     0
sea_level_pressure    0
wind_direction        0
wind_speed            0
site_id               0
timestamp             0
dtype: int64

Az időjárás adatszetekben további hiányzó adat nem maradt. Lássuk az épület adatszettet:

In [49]:
df_building_red.isna().sum()
Out[49]:
site_id           0
building_id       0
primary_use       0
square_feet       0
year_built      774
floor_count    1094
dtype: int64
In [50]:
imp = IterativeImputer(random_state=0)
imp.fit(df_building_red.loc[:, ~df_building_red.columns.isin(['primary_use'])])
df_building_imp = imp.transform(df_building_red.loc[:, ~df_building_red.columns.isin(['primary_use'])])

columns = ['site_id', 'building_id', 'square_feet', 'year_built', 'floor_count']
df_building_imp = pd.DataFrame(df_building_imp)
df_building_imp.columns = columns
df_building_imp = df_building_imp.assign(primary_use = df_building_red["primary_use"])
In [51]:
df_building_imp.isna().sum()
Out[51]:
site_id        0
building_id    0
square_feet    0
year_built     0
floor_count    0
primary_use    0
dtype: int64

Az épület adatszetben is pótoltuk a hiányzó adatokat. Most már egyesíthetjük újra az adatszetteket:

In [52]:
df_train_red["timestamp"] = pd.to_datetime(df_train_red["timestamp"])
df_test_red["timestamp"] = pd.to_datetime(df_test_red["timestamp"])

train_full = df_train_red.merge(df_building_imp, on='building_id', how='left')
test_full = df_test_red.merge(df_building_imp, on='building_id', how='left')

train_full = train_full.merge(df_weather_train_imp, on=['site_id', 'timestamp'], how='left')
test_full = test_full.merge(df_weather_test_imp, on=['site_id', 'timestamp'], how='left')

train_full.dropna(subset=['air_temperature', 'cloud_coverage', 'dew_temperature', 'precip_depth_1_hr', 'sea_level_pressure', 
'wind_direction', 'wind_speed'], how='all', inplace=True)

test_full.dropna(subset=['air_temperature', 'cloud_coverage', 'dew_temperature', 'precip_depth_1_hr', 'sea_level_pressure', 
'wind_direction', 'wind_speed'], how='all', inplace=True)

display(train_full.iloc[:10,:])
display(test_full.iloc[:10,:])
building_id meter timestamp meter_reading site_id square_feet year_built floor_count primary_use air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 0 2016-01-01 0.0 0.0 7432.0 2008.0 6.866956 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
1 1 0 2016-01-01 0.0 0.0 2720.0 2004.0 6.775148 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
2 2 0 2016-01-01 0.0 0.0 5376.0 1991.0 6.801820 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
3 3 0 2016-01-01 0.0 0.0 23685.0 2002.0 7.123003 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
4 4 0 2016-01-01 0.0 0.0 116607.0 1975.0 8.696739 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
5 5 0 2016-01-01 0.0 0.0 8000.0 2000.0 6.837072 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
6 6 0 2016-01-01 0.0 0.0 27926.0 1981.0 7.156437 Lodging/residential 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
7 7 0 2016-01-01 0.0 0.0 121074.0 1989.0 8.768826 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
8 8 0 2016-01-01 0.0 0.0 60809.0 2003.0 7.734213 Education 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
9 9 0 2016-01-01 0.0 0.0 27000.0 2010.0 7.150153 Office 25.0 6.0 20.0 -1.0 1019.5 0.0 0.0
row_id building_id meter timestamp site_id square_feet year_built floor_count primary_use air_temperature cloud_coverage dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed
0 0 0 0 2017-01-01 0.0 7432.0 2008.0 6.866956 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
1 1 1 0 2017-01-01 0.0 2720.0 2004.0 6.775148 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
2 2 2 0 2017-01-01 0.0 5376.0 1991.0 6.801820 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
3 3 3 0 2017-01-01 0.0 23685.0 2002.0 7.123003 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
4 4 4 0 2017-01-01 0.0 116607.0 1975.0 8.696739 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
5 5 5 0 2017-01-01 0.0 8000.0 2000.0 6.837072 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
6 6 6 0 2017-01-01 0.0 27926.0 1981.0 7.156437 Lodging/residential 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
7 7 7 0 2017-01-01 0.0 121074.0 1989.0 8.768826 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
8 8 7 1 2017-01-01 0.0 121074.0 1989.0 8.768826 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
9 9 8 0 2017-01-01 0.0 60809.0 2003.0 7.734213 Education 17.796875 4.0 11.703125 0.0 1021.5 100.0 3.599609
In [53]:
test_full.isna().sum()
Out[53]:
row_id                0
building_id           0
meter                 0
timestamp             0
site_id               0
square_feet           0
year_built            0
floor_count           0
primary_use           0
air_temperature       0
cloud_coverage        0
dew_temperature       0
precip_depth_1_hr     0
sea_level_pressure    0
wind_direction        0
wind_speed            0
dtype: int64

Az idősor esetében problémát jelent, hogy az egyes helyszínek szerinti időbélyeg nem lokális idő szerint került megadásra, ami jelentős torzítást okozhat az energiafogyasztás prediktálásában, hiszen a fogyasztás változása a helyi időt követi, ahogy arra erediteleg az alábbi kernel felhívta a figyelmet: https://www.kaggle.com/nz0722/aligned-timestamp-lgbm-by-meter-type Mivel az épületek geo adatai nem állnak rendelkezésre, így a korrekciót a hőmérsékleti napi csúcsidőszakok alapján lehet megtenni.

In [54]:
weather = pd.concat([df_weather_train_red, df_weather_test_red],ignore_index=True)
weather_key = ['site_id', 'timestamp']

temp_skeleton = weather[weather_key + ['air_temperature']].drop_duplicates(subset=weather_key).sort_values(by=weather_key).copy()
temp_skeleton["timestamp"] = pd.to_datetime(temp_skeleton["timestamp"])

temp_skeleton['temp_rank'] = temp_skeleton.groupby(['site_id', temp_skeleton.timestamp.dt.date])['air_temperature'].rank('average')


df_2d = temp_skeleton.groupby(['site_id', temp_skeleton.timestamp.dt.hour])['temp_rank'].mean().unstack(level=1)

site_ids_offsets = pd.Series(df_2d.values.argmax(axis=1) - 14)
site_ids_offsets.index.name = 'site_id'

def timestamp_align(df):
    df['offset'] = df.site_id.map(site_ids_offsets)
    df['timestamp_aligned'] = (df.timestamp - pd.to_timedelta(df.offset, unit='H'))
    df['timestamp'] = df['timestamp_aligned']
    del df['timestamp_aligned']
    return df
In [55]:
# Változtatás előtti utolsó 5 adatsor
train.tail()
Out[55]:
building_id meter timestamp meter_reading site_id primary_use square_feet year_built floor_count air_temperature ... dew_temperature precip_depth_1_hr sea_level_pressure wind_direction wind_speed hour day weekend month log_meter_reading
20216095 1444 0 2016-12-31 23:00:00 8.750000 15 Entertainment/public assembly 19619 1914.0 NaN 1.700195 ... -5.601562 -1.0 1008.5 180.0 8.796875 23 31 5 12 2.277267
20216096 1445 0 2016-12-31 23:00:00 4.825000 15 Education 4298 NaN NaN 1.700195 ... -5.601562 -1.0 1008.5 180.0 8.796875 23 31 5 12 1.762159
20216097 1446 0 2016-12-31 23:00:00 0.000000 15 Entertainment/public assembly 11265 1997.0 NaN 1.700195 ... -5.601562 -1.0 1008.5 180.0 8.796875 23 31 5 12 0.000000
20216098 1447 0 2016-12-31 23:00:00 159.574997 15 Lodging/residential 29775 2001.0 NaN 1.700195 ... -5.601562 -1.0 1008.5 180.0 8.796875 23 31 5 12 5.078761
20216099 1448 0 2016-12-31 23:00:00 2.850000 15 Office 92271 2001.0 NaN 1.700195 ... -5.601562 -1.0 1008.5 180.0 8.796875 23 31 5 12 1.348073

5 rows × 21 columns

In [56]:
# időbélyeg változó korrekciója
train = timestamp_align(train)
test = timestamp_align(test)
In [57]:
# Változtatás előtti utolsó 5 adatsor
train.tail()
Out[57]:
building_id meter timestamp meter_reading site_id primary_use square_feet year_built floor_count air_temperature ... precip_depth_1_hr sea_level_pressure wind_direction wind_speed hour day weekend month log_meter_reading offset
20216095 1444 0 2016-12-31 17:00:00 8.750000 15 Entertainment/public assembly 19619 1914.0 NaN 1.700195 ... -1.0 1008.5 180.0 8.796875 23 31 5 12 2.277267 6
20216096 1445 0 2016-12-31 17:00:00 4.825000 15 Education 4298 NaN NaN 1.700195 ... -1.0 1008.5 180.0 8.796875 23 31 5 12 1.762159 6
20216097 1446 0 2016-12-31 17:00:00 0.000000 15 Entertainment/public assembly 11265 1997.0 NaN 1.700195 ... -1.0 1008.5 180.0 8.796875 23 31 5 12 0.000000 6
20216098 1447 0 2016-12-31 17:00:00 159.574997 15 Lodging/residential 29775 2001.0 NaN 1.700195 ... -1.0 1008.5 180.0 8.796875 23 31 5 12 5.078761 6
20216099 1448 0 2016-12-31 17:00:00 2.850000 15 Office 92271 2001.0 NaN 1.700195 ... -1.0 1008.5 180.0 8.796875 23 31 5 12 1.348073 6

5 rows × 22 columns

In [58]:
# Néhány további változó hozzáadása
def transform_df(df):
    df['hour'] = np.uint8(df['timestamp'].dt.hour)
    df['day'] = np.uint8(df['timestamp'].dt.day)
    df['weekday'] = np.uint8(df['timestamp'].dt.weekday)
    df['month'] = np.uint8(df['timestamp'].dt.month)
    
    df['square_feet'] = np.log(df['square_feet'])
    
    return df
In [59]:
train_full = transform_df(train_full)
test_full = transform_df(test_full)

Model illesztése

In [60]:
cat_feat = ['meter', "site_id", "building_id", "primary_use", "hour", "weekday", "wind_direction"]
In [61]:
# Encoding categorical features to use in the lightgbm model
le = LabelEncoder()
train_full['primary_use'] = le.fit_transform(train_full['primary_use'])
test_full['primary_use'] = le.fit_transform(test_full['primary_use'])
In [62]:
train_full.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 20125605 entries, 0 to 20216099
Data columns (total 20 columns):
building_id           int16
meter                 int8
timestamp             datetime64[ns]
meter_reading         float32
site_id               float64
square_feet           float64
year_built            float64
floor_count           float64
primary_use           int32
air_temperature       float64
cloud_coverage        float64
dew_temperature       float64
precip_depth_1_hr     float64
sea_level_pressure    float64
wind_direction        float64
wind_speed            float64
hour                  uint8
day                   uint8
weekday               uint8
month                 uint8
dtypes: datetime64[ns](1), float32(1), float64(11), int16(1), int32(1), int8(1), uint8(4)
memory usage: 2.2 GB
In [63]:
target = np.log1p(train_full['meter_reading'])
train_full = train_full.drop(['meter_reading', 'timestamp'], axis = 1)
test_full = test_full.drop(['row_id', 'timestamp'], axis = 1)
In [65]:
params = {
            'boosting_type': 'gbdt',
            'objective': 'regression',
            'metric': {'rmse'},
            'subsample': 0.4,
            'subsample_freq': 1,
            'learning_rate': 0.25,
            'num_leaves': 31,
            'feature_fraction': 0.8,
            'lambda_l1': 1,
            'lambda_l2': 1
            }

folds = 4
seed = 55
kf = StratifiedKFold(n_splits=folds, shuffle=True, random_state=seed)

models = []

## stratify data by building_id
for train_index, val_index in tqdm(kf.split(train_full, train_full['building_id']), total=folds):
    train_X = train_full.iloc[train_index]
    val_X = train_full.iloc[val_index]
    train_y = target.iloc[train_index]
    val_y = target.iloc[val_index]
    lgb_train = lgb.Dataset(train_X, train_y, categorical_feature=cat_feat)
    lgb_eval = lgb.Dataset(val_X, val_y, categorical_feature=cat_feat)
    gbm = lgb.train(params,
                lgb_train,
                num_boost_round=500,
                valid_sets=(lgb_train, lgb_eval),
                early_stopping_rounds=100,
                verbose_eval = 100)
    models.append(gbm)
  0%|                                                                                            | 0/4 [00:00<?, ?it/s]c:\users\zsolt\appdata\local\programs\python\python37\lib\site-packages\lightgbm\basic.py:1291: UserWarning: Using categorical_feature in Dataset.
  warnings.warn('Using categorical_feature in Dataset.')
Training until validation scores don't improve for 100 rounds
[100]	training's rmse: 0.893653	valid_1's rmse: 0.893798
[200]	training's rmse: 0.820671	valid_1's rmse: 0.821245
[300]	training's rmse: 0.779437	valid_1's rmse: 0.78069
[400]	training's rmse: 0.751706	valid_1's rmse: 0.753411
[500]	training's rmse: 0.73305	valid_1's rmse: 0.735644
Did not meet early stopping. Best iteration is:
[500]	training's rmse: 0.73305	valid_1's rmse: 0.735644
 25%|████████████████████▊                                                              | 1/4 [07:54<23:44, 474.93s/it]
Training until validation scores don't improve for 100 rounds
[100]	training's rmse: 0.897335	valid_1's rmse: 0.896606
[200]	training's rmse: 0.817278	valid_1's rmse: 0.817162
[300]	training's rmse: 0.777813	valid_1's rmse: 0.77862
[400]	training's rmse: 0.748991	valid_1's rmse: 0.750539
[500]	training's rmse: 0.729191	valid_1's rmse: 0.731526
Did not meet early stopping. Best iteration is:
[500]	training's rmse: 0.729191	valid_1's rmse: 0.731526
 50%|█████████████████████████████████████████▌                                         | 2/4 [11:24<13:10, 395.31s/it]
Training until validation scores don't improve for 100 rounds
[100]	training's rmse: 0.896349	valid_1's rmse: 0.899112
[200]	training's rmse: 0.824296	valid_1's rmse: 0.827765
[300]	training's rmse: 0.783422	valid_1's rmse: 0.787847
[400]	training's rmse: 0.756726	valid_1's rmse: 0.761942
[500]	training's rmse: 0.732782	valid_1's rmse: 0.738906
Did not meet early stopping. Best iteration is:
[500]	training's rmse: 0.732782	valid_1's rmse: 0.738906
 75%|██████████████████████████████████████████████████████████████▎                    | 3/4 [14:51<05:38, 338.79s/it]
Training until validation scores don't improve for 100 rounds
[100]	training's rmse: 0.896772	valid_1's rmse: 0.898191
[200]	training's rmse: 0.816937	valid_1's rmse: 0.818435
[300]	training's rmse: 0.77639	valid_1's rmse: 0.778408
[400]	training's rmse: 0.748649	valid_1's rmse: 0.75134
[500]	training's rmse: 0.724508	valid_1's rmse: 0.728177
Did not meet early stopping. Best iteration is:
[500]	training's rmse: 0.724508	valid_1's rmse: 0.728177
100%|███████████████████████████████████████████████████████████████████████████████████| 4/4 [18:21<00:00, 275.41s/it]
In [66]:
plt.rcParams['figure.figsize'] = (18,10)
for model in range(0,3):
    lgb.plot_importance(models[model], importance_type='gain')
    plt.show()

500 iteráció után a négyzetes középhiba (Root mean squered error (RMSE)) 0.73 körüli értékre esik mind a négy adathalmazon, amelyeket a k-fold keresztvalidáció során hoztunk létre. További iterációk révén a hiba mértéke tovább csökkenthető lenne, de most erre nem tér ki a modellezés. A legfontosabb változók az épületID és a épületnagyság voltak.

Jelen modellépítés itt véget ér, ugyanakkor a további fejlesztésre rengeteg tér maradt, többek között, de egyáltalán nem kizárólagosan:

  • Kiugró értékek eltávolítása

  • Modellparaméterek finomhangolása

  • Többszörös modellépítés, helyszínenként, mérőegységenként, stb.

  • További algoritmusok kipróbálása

  • </ul> A további lépésekhez élvezetes munkát és kitartást!

In [ ]: